//
// Created by Martin on 2023/4/11.
//
#include "OsFile.h"

#include <cassert>
#include <cstring>

namespace ftp {
namespace filesystem {

const char* fileStatusToString(FileStatus_t status)
{
    switch (status) {
        case kFileStatusIdle:
            return "idle";
        case kFileStatusRead:
            return "read";
        case kFileStatusWrite:
            return "write";
    }
    return "";
}

FileStatus_t toFileStatus(const char* str)
{
    assert(str != nullptr);
    auto res = kFileStatusIdle;
    if (strcasecmp(str, "read") == 0) {
        res = kFileStatusRead;
    }
    else if (strcasecmp(str, "write") == 0) {
        res = kFileStatusWrite;
    }
    return res;
}

OsFile &OsFile::getInstance()
{
    static OsFile osFile;
    return osFile;
}

bool OsFile::allocFile(const std::string &pathname, FileStatus_t status)
{
    std::lock_guard<std::mutex> lock(mutex_);
    return doAllocFile(pathname, status);;
}

bool OsFile::allocFile(const std::string& pathname, const char* status)
{
    std::lock_guard<std::mutex> lock(mutex_);
    return doAllocFile(pathname, toFileStatus(status));
}

void OsFile::freeFile(const std::string &pathname)
{
    std::lock_guard<std::mutex> lock(mutex_);
    doFreeFile(pathname);
}

bool OsFile::doAllocFile(const std::string &pathname, FileStatus_t status)
{
    bool res = false;
    auto it = files_.find(pathname);
    if (it == files_.end()) {
        files_[pathname] = OsFileStatus(status);
        return true;
    }

    if (it->second.numUsers < 0) { // Occupied by other writes
        return false;
    }

    switch (status) {
        case kFileStatusIdle:
            res = true;
            break;
        case kFileStatusRead:
            ++it->second.numUsers;
            res = true;
            break;
        case kFileStatusWrite:
            if (it->second.numUsers == 0) {
                --it->second.numUsers;
                res = true;
            }
            break;
    }

    return res;
}

void OsFile::doFreeFile(const std::string& pathname)
{
    auto it = files_.find(pathname);
    if (it == files_.end()) {
        return;
    }

    if (it->second.numUsers > 0)
        --it->second.numUsers;
    else if (it->second.numUsers < 0)
        it->second.numUsers = 0;

    if (it->second.numUsers == 0) {
        files_.erase(pathname);
    }
}

} // namespace filesystem
} // namespace ftp
